#include "Lex.h"

#include <QString>

Token tokenList[1024];// token表

// digit check
// 检查是不是数字
bool isDigit(char ch)
{
    if (ch >= '0' && ch <= '9')
        return true;
    else
        return false;
}

// letter check
// 检查是不是字母
bool isLetter(char ch)
{
    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
        return true;
    else
        return false;
}

// blank check
// 检查是不是需要blank掉
bool isBlank(char ch)
{

    if (ch == ' ') // 空格
        return true;
    else if (ch == '\n') // 换行
        return true;
    else if (ch == '\t') // 制表符
        return true;
    else if (ch == '\r') // 回车
        return true;
    else
        return false;
}

// key check
// 检查是不是保留字
bool isReserved(string ch)
{
    for (int i = 0; i < 21; i++)
        if (ch == reservedWords[i].str)
            return true;
    return false;
}

// find the correspond reversed word
// 在保留字表中找到对应的编号
struct Word getReserved(string s)
{
    for (int i = 0; i < 21; i++)
    {
        if (reservedWords[i].str == s)
            return reservedWords[i];
    }
}

// operator check
// 检查是不是运算符
bool isOperator(char ch)
{
    if (ch == '+')
        return true;
    else if (ch == '-')
        return true;
    else if (ch == '*')
        return true;
    else if (ch == '/')
        return true;
    else if (ch == '<')
        return true;
    else if (ch == '=')
        return true;
    else
        return false;
}

// separater cehck
// 检查是不是分隔符
bool isSeparater(char ch)
{
    if (ch == ';')
        return true;
    else if (ch == ',')
        return true;
    else if (ch == '{')
        return true;
    else if (ch == '}')
        return true;
    else if (ch == '[')
        return true;
    else if (ch == ']')
        return true;
    else if (ch == '(')
        return true;
    else if (ch == ')')
        return true;
    else if (ch == '.')
        return true;
    else if (ch == '\'')
        return true;
    else if (ch == ':')
        return true;
    else
        return false;
}

// output
// 输出到文件
list<string> printTokenList(Token* tokenList)
{
    ofstream output(OUT1);
    string str;
    list<string> ret;
    ret.clear();
    int i = 0;
    while (tokenList[i].wd.tok != ENDFILE)
    {
        str = "";
        output << tokenList[i].line
            << "\t" << std::left << setw(16) << getTokenStr(tokenList[i].wd.tok)
            << "\t" << tokenList[i].wd.str << endl;

        str += to_string(tokenList[i].line)
            + "\t" +  getTokenStr(tokenList[i].wd.tok)
            + "\t" + tokenList[i].wd.str;
        ret.push_back(str);
        i++;
    }
    output << tokenList[i].line
        << "\t" << std::left << setw(16) << getTokenStr(tokenList[i].wd.tok)
        << "\t" << tokenList[i].wd.str << endl;

    str = "";
    str += to_string(tokenList[i].line)
        + "\t" +  getTokenStr(tokenList[i].wd.tok)
        + "\t" + tokenList[i].wd.str + "\n";

    output.close();

    ret.push_back(str);
    return ret;
}


// Lexical analyse
// 词法分析
bool lexicalAnalyse(FILE *fp)
{
    int lineNum = 1; // line number, start from 1
    // 记录行号 在后面写token的时候进行记录

    int index = 0; // tokenList array index
    // 用于token的记录

    char ch = fgetc(fp); // f get char
    // 初始化 取出第一个char

    string record = ""; // 当收集到char 存入string record

    // 当遇到 end of file 时暂停
    while (ch != EOF)
    {

        // letter
        // 检查是不是字母
        if (isLetter(ch))
        {
            record = ""; // 用来记录字符串
            record = record + ch; // 先把第一个字符存进来


            ////////////////取字符串开始////////////////
            ch = fgetc(fp); // 把第二个字符取出来 与上面类似
            while (isLetter(ch) || isDigit(ch)) // 注意 这里无论是字母或数字都可以保存
            {
                record = record + ch; // 存进字符串
                ch = fgetc(fp); // 循环 取出下一个字符
                // 注意 别忘了取出下一个
            }
            ////////////////取字符串结束////////////////
            // 目前 record中已经保存有完整的字符串

            /*
                INID标识符状态
            */
            // 字符串 有可能是ID 也有可能是reversedWord
            if (isReserved(record)) // 检查是ID 还是保留字
            {
                // 是保留字
                tokenList[index].line = lineNum; // 记录行号
                struct Word tempWord = getReserved(record);
                tokenList[index].wd.str = tempWord.str; // 记录字符串
                tokenList[index].wd.tok = tempWord.tok; // 记录token
                index++;
            }
            else
            {
                // 是ID
                tokenList[index].line = lineNum; // 记录行号
                tokenList[index].wd.str = record; // 记录字符串
                tokenList[index].wd.tok = ID; // 记录token
                index++;
            }
        }

        // int
        // 检查是不是数字
        else if (isDigit(ch))
        {
            record = ""; // 同上 用于记录
            record += ch; //


            ////////////////取字符串开始////////////////
            ch = fgetc(fp); // 把第一个取出来

            while (isDigit(ch) || isLetter(ch))
            {
                record += ch;
                ch = fgetc(fp);
            }
            ////////////////取字符串结束////////////////
            // 目前 record中已经保存有完整的字符串


            int flag = 1; // all digit? 目前的版本中 只允许全部为数字的
            for (int i = 0; i < record.length(); i++)
            {
                if (isLetter(record[i])) // 只要有字母 就退出
                {
                    flag = 0;
                    tokenList[index].wd.tok = ERROR;
                    break;
                }
            }
            /*
                INNUM数字状态
            */
            if (flag == 1) // 全部为数字 记作INTC
                tokenList[index].wd.tok = INTC;

            tokenList[index].line = lineNum;
            tokenList[index].wd.str = record;
            index++;
        }

        // operator
        // 运算符检测
        else if (isOperator(ch)) // 检查是不是运算符
        {
            string temp = ""; // char转string
            temp = temp + ch;

            if (temp == "+")
                tokenList[index].wd.tok = PLUS;
            else if (temp == "-")
                tokenList[index].wd.tok = MINUS;
            else if (temp == "*")
                tokenList[index].wd.tok = TIMES;
            else if (temp == "/")
                tokenList[index].wd.tok = OVER;
            else if (temp == "<")
                tokenList[index].wd.tok = LT;
            else if (temp == "=")
                tokenList[index].wd.tok = EQ;

            tokenList[index].line = lineNum;
            tokenList[index].wd.str = temp;

            index++;
            ch = fgetc(fp);
        }

        // separater
        else if (isSeparater(ch)) // 检查是不是分隔符
        {
            record = "";
            // ignore annotation
            // {}注释 忽略掉
            /*
                INCOMMENT注释状态
            */
            if (ch == '{')
            {
                while (ch != '}')
                {
                    ch = fgetc(fp);
                    if (ch == '\n')
                        lineNum += 1;
                }
                ch = fgetc(fp);
            }
            // limit operator
            //
            else if (ch == '.')
            {
                record += ch;
                if ((ch = fgetc(fp)) == '.') // 记录'..'
                {
                    /*
                        INRANGE数组下标界限状态
                    */
                    record += ch;
                    tokenList[index].line = lineNum;
                    tokenList[index].wd.str = record;
                    tokenList[index].wd.tok = UNDERANGE;
                    index++;
                    ch = fgetc(fp);
                }
                else // 记录'.'
                {
                    tokenList[index].line = lineNum;
                    tokenList[index].wd.str = record;
                    tokenList[index].wd.tok = DOT;
                    index++;
                }
            }
            // string
            /*
                INCHAR字符标志状态
            */
            else if (ch == '\'')
            {
                tokenList[index].line = lineNum;
                string temp = "";
                temp = temp + ch;
                tokenList[index].wd.tok = CHARC;
                while ((ch = fgetc(fp)) != '\'') //读入字符串
                {
                    record += ch;
                }
                tokenList[index].wd.str = record;
                index++;
                ch = fgetc(fp);
            }
            // :
            else if (ch == ':')
            {
                record += ch;
                if ((ch = fgetc(fp)) == '=')
                {
                    /*
                        INASSIGN赋值状态
                    */
                    record += ch;
                    tokenList[index].line = lineNum;
                    tokenList[index].wd.str = record;//:=
                    tokenList[index].wd.tok = ASSIGN;
                    index++;
                    ch = fgetc(fp);
                }
                else
                {
                    tokenList[index].line = lineNum;
                    tokenList[index].wd.str = record;
                    tokenList[index].wd.tok = COLON;
                    index++;

                }
            }
            //
            else
            {
                tokenList[index].line = lineNum;
                string temp = "";
                temp = temp + ch;
                tokenList[index].wd.str = temp;

                if (temp == "(")
                    tokenList[index].wd.tok = LPAREN;
                else if (temp == ")")
                    tokenList[index].wd.tok = RPAREN;
                else if (temp == "[")
                    tokenList[index].wd.tok = LMIDPAREN;
                else if (temp == "]")
                    tokenList[index].wd.tok = RMIDPAREN;
                else if (temp == ";")
                    tokenList[index].wd.tok = SEMI;
                else if (temp == ",")
                    tokenList[index].wd.tok = COMMA;

                index++;
                ch = fgetc(fp);
            }
        }

        // blank
        // 检查是不是 blank
        // 空格 制表符 回车 换行
        else if (isBlank(ch))
        {
            if (ch == '\n') // 换行符特殊处理 行号+1
                lineNum += 1;
            else
                ;
            ch = fgetc(fp);
        }

        // unknown
        else
        {
            string t = "Line ";
            t += to_string(lineNum);
            t += " ";
            t += "\"";
            t += ch;
            t += "\" ";
            t += "UnknownToken Error!\n";
            printErrorMsg(t);
            return false;
        }
    }
    /*
        DONE完成状态
    */
    tokenList[index].line = lineNum;
    tokenList[index].wd.str = ch;
    tokenList[index].wd.tok = ENDFILE;
    return true;
}

string getTokenStr(LexType tkt)
{
    switch (tkt) {
    case ENDFILE:
        return string("ENDFILE");
        break;
    case ERROR:
        return string("ERROR");
        break;
    case PROGRAM:
        return string("PROGRAM");
        break;
    case PROCEDURE:
        return string("PROCEDURE");
        break;
    case TYPE:
        return string("TYPE");
        break;
    case VAR:
        return string("VAR");
        break;
    case IF:
        return string("IF");
        break;
    case THEN:
        return string("THEN");
        break;
    case ELSE:
        return string("ELSE");
        break;
    case FI:
        return string("FI");
        break;
    case WHILE:
        return string("WHILE");
        break;
    case DO:
        return string("DO");
        break;
    case ENDWH:
        return string("ENDWH");
        break;
    case BEGIN:
        return string("BEGIN");
        break;
    case END:
        return string("END");
        break;
    case READ:
        return string("READ");
        break;
    case WRITE:
        return string("WRITE");
        break;
    case ARRAY:
        return string("ARRAY");
        break;
    case OF:
        return string("OF");
        break;
    case RECORD:
        return string("RECORD");
        break;
    case RETURN:
        return string("RETURN");
        break;
    case INTEGER:
        return string("INTEGER");
        break;
    case CHAR:
        return string("CHAR");
        break;
    case ID:
        return string("ID");
        break;
    case INTC:
        return string("INTC");
        break;
    case CHARC:
        return string("CHARC");
        break;
    case ASSIGN:
        return string("ASSIGN");
        break;
    case EQ:
        return string("EQ");
        break;
    case LT:
        return string("LT");
        break;
    case PLUS:
        return string("PLUS");
        break;
    case MINUS:
        return string("MINUS");
        break;
    case TIMES:
        return string("TIMES");
        break;
    case OVER:
        return string("OVER");
        break;
    case LPAREN:
        return string("LPAREN");
        break;
    case RPAREN:
        return string("RPAREN");
        break;
    case DOT:
        return string("DOT");
        break;
    case COLON:
        return string("COLON");
        break;
    case SEMI:
        return string("SEMI");
        break;
    case COMMA:
        return string("COMMA");
        break;
    case LMIDPAREN:
        return string("LMIDPAREN");
        break;
    case RMIDPAREN:
        return string("RMIDPAREN");
        break;
    case UNDERANGE:
        return string("UNDERANGE");
        break;
    case LAMBDA:
        return string("LAMBDA");
        break;
    default:
        return string("Invalid Lex");
        break;
    }
}
